package com.jbp.aspect;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jbp.exception.RRException;
import com.jbp.model.User;
import com.jbp.redis.RedisConfigService;
import com.jbp.service.PropertyService;
import com.jbp.service.UserService;
import com.jbp.utils.Constant;
import com.jbp.utils.PropertyCacheUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/**
 * 对请求体进行解密
 */
@Component
@ControllerAdvice
public class DecryptRequestBodyAdvice extends RequestBodyAdviceAdapter {

    private static final Logger logger = LoggerFactory.getLogger(DecryptRequestBodyAdvice.class);

    @Autowired
    private PropertyService propertyService;

    @Autowired
    private RedisConfigService redisConfigService;

    @Autowired
    private UserService userService;

    private HttpServletRequest request;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        boolean decryptBody = false;
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        this.request = (HttpServletRequest)requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

        String noEncryptConfig = PropertyCacheUtil.getCachedProperty(propertyService, redisConfigService,
                "no_encrypt_config");
        // 如果请求/rest/article/save，这里的uri是save，不满足需求
        String uri = this.request.getRequestURI();
        // 开启了加解密，并且uri不在黑名单里面，才会去解密
        boolean isUriInConfig = uriInConfig(noEncryptConfig, uri);

        String encrypt = request.getHeader("encrypt");
        String debug = PropertyCacheUtil.getCachedProperty(propertyService, redisConfigService,
                "debug");
        //关闭debug模式
        if ("false".equals(debug) && !"1".equalsIgnoreCase(encrypt) && !isUriInConfig) {
            throw new RRException("接口未加密");
        }

        if ("1".equalsIgnoreCase(encrypt) && !isUriInConfig) {
            decryptBody = true;
        }
        return decryptBody;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        // body不为空时，才会执行下面的代码，比如GET请求不会执行下面的代码
        boolean useRsaDecrypt = false;
        String rsaEncryptConfig = PropertyCacheUtil.getCachedProperty(propertyService, redisConfigService,
                "rsa_encrypt_config");

        String uri = this.request.getRequestURI();
        // 在rsa解密的白名单里面，才会使用rsa解密，否则使用AES解密
        boolean isUriInConfig = uriInConfig(rsaEncryptConfig, uri);

        if (rsaEncryptConfig != null && isUriInConfig) {
            useRsaDecrypt = true;
        }

        //if("login".equals(methodParameter.getMethod().getName())) {
        if(useRsaDecrypt) {
            return new DecryptInputMessageRSA(httpInputMessage, this.propertyService);
        } else {

            Object userId = this.request.getAttribute(Constant.USER_ID);
            if (userId == null) {
                return httpInputMessage;
            } else {
                User user = userService.getOne(new QueryWrapper<User>().eq("id", userId));
                if (user == null) {
                    return httpInputMessage;
                } else {
                    return new DecryptInputMessageAES(httpInputMessage, user);
                }
            }
        }
    }

    private boolean uriInConfig(String configString, String uri) {
        if(configString == null) {
            return false;
        }
        List<String> configList =  Arrays.asList(configString.split(","));
        List<String> stripedConfigList = new ArrayList<>();
        for (String config: configList) {
            if(config != null) {
                stripedConfigList.add(config);
            }
        }
        return stripedConfigList.contains(uri);
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }
}
